//=========================================================
// File			- rect.h
// Author		- Jesse Lampert
// Created	- 04/18/06
//=========================================================

#ifndef __RECT_H
#define __RECT_H

//=== Include =============================================
#include "macros.h"
#include "vec2.h"
#include "f32.h"

//=== Classes, Structures and Unions ======================

//=========================================================
// Class	- _Point
// Purpose	- 
// Author	- Yuhui Wang
//=========================================================
struct _Point {
	int16 x, y;
	inline _Point( void )
	{
		x = y = 0;
	}
	
	inline _Point(int16 x, int16 y)
	{
		this->x = x;
		this->y = y;
	}
	
	inline _Point(const _Point* point)
	{
		this->x = point->x;
		this->y = point->y;
	}
	
	inline _Point & Set( int16 x, int16 y )
	{
		this->x = x;
		this->y = y;
		return *this;
	};
	
	inline _Point & Set( const _Point * pRect )
	{
		x = pRect->x;
		y = pRect->y;		
		return *this;
	};
	
	inline const _Point & operator=( const _Point & r )
	{
		if( this == &r )
			return *this;
		
		x = r.x;
		y = r.y;
		
		return *this;
	};
	
	inline boolean operator==( const _Point & r ) const
	{
		return ( x == r.x && y == r.y  );
	}
	
	inline boolean operator!=( const _Point & r ) const
	{
		return !( *this == r );
	}
	
	inline void Clear( void )
	{
		x = y =  0;
	}
};

//=========================================================
// Class		- _Rect
// Purpose	- slight extension of
//=========================================================
struct _Rect
{
	int16 x, y, dx, dy;
	
	inline _Rect( void )
	{
		x = y = dx = dy = 0;
	}
	
	inline _Rect( const AEERect& rect )
	{
		x = rect.x;
		y = rect.y;
		dx = rect.dx;
		dy = rect.dy;
	}

	inline _Rect( int16 x, int16 y, int16 dx, int16 dy )
	{
		this->x = x;
		this->y = y;
		this->dx = dx;
		this->dy = dy;
	};

	inline _Rect( const _Rect * pRect )
	{
		x = pRect->x;
		y = pRect->y;
		dx = pRect->dx;
		dy = pRect->dy;
	};

	inline _Rect & Set( int16 x, int16 y, int16 dx, int16 dy )
	{
		this->x = x;
		this->y = y;
		this->dx = dx;
		this->dy = dy;

		return *this;
	};

	inline _Rect & Set( const _Rect * pRect )
	{
		x = pRect->x;
		y = pRect->y;
		dx = pRect->dx;
		dy = pRect->dy;

		return *this;
	};

	inline const _Rect & operator=( const _Rect & r )
	{
		if( this == &r )
			return *this;

		x = r.x;
		y = r.y;
		dx = r.dx;
		dy = r.dy;

		return *this;
	};

	inline boolean operator==( const _Rect & r ) const
	{
		return ( x == r.x && y == r.y && dx == r.dx && dy == r.dy );
	}

	inline boolean operator!=( const _Rect & r ) const
	{
		return !( *this == r );
	}

	inline void Clear( void )
	{
		x = y = dx = dy = 0;
	}

	inline int Area( void ) const
	{
		return dx * dy;
	}

	inline void CenterToX( int16 dx )
	{
		x = ( dx - this->dx ) / 2;
	}

	inline void CenterToY( int16 dy )
	{
		y = ( dy - this->dy ) / 2;
	}

	inline void CenterTo( int16 dx, int16 dy )
	{
		x = ( dx - this->dx ) / 2;
		y = ( dy - this->dy ) / 2;
	}

	inline void CenterTo( _Rect & r )
	{
		x = r.x + ( r.dx - this->dx ) / 2;
		y = r.y + ( r.dy - this->dy ) / 2;
	}

	inline int16 CenterX( void ) const
	{
		return x + dx / 2;
	}

	inline int16 CenterY( void ) const
	{
		return y + dy / 2;
	}

	inline void Expand( int16 amount )
	{
		x -= amount;
		y -= amount;
		dx += amount * 2;
		dy += amount * 2;
	};

	inline void Move( int16 dx, int16 dy )
	{
		x += dx;
		y += dy;
	};

	inline void MoveUp( int16 padding )
	{
		y -= dy + padding;
	};
  
	inline void MoveDown( int16 padding )
	{
		y += dy + padding;
	};

	inline void MoveLeft( int16 padding )
	{
		x -= dx + padding;
	};

	inline void MoveRight( int16 padding )
	{
		x += dx + padding;
	};

	inline void FitToRect( const _Rect * pRect, int16 padding )
	{
		// clamp to the right
		if( x + dx + padding > pRect->x + pRect->dx )
			x = ( pRect->x + pRect->dx ) - ( dx + padding );
		if( y + dy + padding > pRect->y + pRect->dy )
			y = ( pRect->y + pRect->dy ) - ( dy + padding );

		// clamp to the left
		if( x < pRect->x + padding )
			x = pRect->x + padding;
		if( y < pRect->y + padding )
			y = pRect->y + padding;
	}

	inline void Combine( const _Rect * pRect )
	{
		int16 x, y;
		int16 inputRightX, outputRightX;
		int16 inputBottomY, outputBottomY;

		// nothing to combine
		if( !pRect->dx || !pRect->dy )
			return;

		if( !this->dx || !this->dy )
		{
			*this = *pRect;
			return;
		}

		// adjust top left point
		if( pRect->x < this->x )
			x = pRect->x;
		else x = this->x;

		if( pRect->y < this->y )
			y = pRect->y;
		else y = this->y;

		inputRightX = pRect->x + pRect->dx;
		inputBottomY = pRect->y + pRect->dy;
		outputRightX = this->x + this->dx;
		outputBottomY = this->y + this->dy;

		if( inputRightX > outputRightX )
			outputRightX = inputRightX;
		if( inputBottomY > outputBottomY )
			outputBottomY = inputBottomY;

		// fix up output rect
		this->x = x;
		this->y = y;
		this->dx = outputRightX - this->x;
		this->dy = outputBottomY - this->y;
	}

	inline boolean Intersects( const _Rect * pRect ) const
	{
		if( ( x >= pRect->x && pRect->x + pRect->dx >= x ) || ( x < pRect->x && x + dx >= pRect->x ) )
		{
			if( ( y >= pRect->y && pRect->y + pRect->dy >= y ) || ( y < pRect->y && y + dy >= pRect->y ) )
				return TRUE;
		}

		return FALSE;
	}

	inline boolean Contains( const _Rect * pRect ) const
	{
		if( x <= pRect->x )
		{
			if( y <= pRect->y )
			{
				if( y + dy >= pRect->y + pRect->dy )
				{
					if( x + dx >= pRect->x + pRect->dx )
						return TRUE;
				}
			}
		}

		return FALSE;
	}

	inline boolean Contains( int16 x, int16 y ) const
	{
		if( this->x <= x )
		{
			if( this->y <= y )
			{
				if( this->x + this->dx >= x )
				{
					if( this->y + this->dy >= y )					
						return TRUE;
				}
			}
		}

		return FALSE;
	}

	inline void Subtract( const _Rect * pRect )
	{
		if( pRect->x < x )
		{
			int16 diff = x - pRect->x;
			x += diff;
			dx -= diff;
		}
		else if( pRect->x > x )
			dx -= pRect->dx;
		else if( pRect->y < y )
		{
			int16 diff = y - pRect->y;
			y += diff;
			dy -= diff;
		}
		else if( pRect->y > y )
			dy -= pRect->dy;
	}

	inline void Clip( const _Rect * pRect )
	{
		ClipRegionToRegion( pRect->x, pRect->dx, x, dx );
		ClipRegionToRegion( pRect->y, pRect->dy, y, dy );

		if( dx == 0 || dy == 0 )
			Clear( );
	}

	inline void GetRotatedArea( f32 angle, _Rect * pRect ) const
	{
		CMatrix2d rotation;
		rotation.LoadIdentity( );
		rotation.Rotate( angle.Value );

		vec2 leftTop( (f32)x, (f32)y );
		vec2 rightTop( (f32)(x + dx), (f32)y );
		vec2 leftBottom( (f32)x, (f32)(y + dy) );
		vec2 rightBottom( (f32)(x + dx), (f32)(y + dy) );

		leftTop *= rotation;
		rightTop *= rotation;
		leftBottom *= rotation;
		rightBottom *= rotation;

		int16 minX = MIN( MIN( leftTop.x.Int16( ), rightTop.x.Int16( ) ), MIN( leftBottom.x.Int16( ), rightBottom.x.Int16( ) ) );
		int16 maxX = MAX( MAX( leftTop.x.Int16( ), rightTop.x.Int16( ) ), MAX( leftBottom.x.Int16( ), rightBottom.x.Int16( ) ) );

		int16 minY = MIN( MIN( leftTop.y.Int16( ), rightTop.y.Int16( ) ), MIN( leftBottom.y.Int16( ), rightBottom.y.Int16( ) ) );
		int16 maxY = MAX( MAX( leftTop.y.Int16( ), rightTop.y.Int16( ) ), MAX( leftBottom.y.Int16( ), rightBottom.y.Int16( ) ) );

		pRect->x = minX;
		pRect->y = minY;
		pRect->dx = maxX - minX;
		pRect->dy = maxY - minY;
	}

	private:
		inline void ClipRegionToRegion( int16 start1, int16 size1, int16 & start2, int16 & size2 )
		{
			if( start1 + size1 > start2 && start1 < start2 + size2 )
			{
				// save the size
				int initialSize2 = size2;

				// rectangles overlap, clip pRect to pClip
				if( start2 + size2 > start1 + size1 )
					size2 = (start1 + size1) - start2;
			
				if( start2 < start1 )
				{
					size2 -= start1 - start2;
					start2 = start1;
				}

				// don't allow the sizes to change sign
				if( initialSize2 > 0 && size2 < 0 || initialSize2 < 0 && size2 > 0 )
					size2 = 0;
			}
			else size2 = 0;
		}
};

#endif // __RECT_H
